package cn.dapeng.rpc.client;

import cn.dapeng.rpc.client.pool.ClientFactoryV2;
import cn.dapeng.rpc.common.Constant;
import cn.dapeng.rpc.common.RequestTypeEnum;
import cn.dapeng.rpc.common.RpcRequest;
import cn.dapeng.rpc.common.RpcResponse;
import cn.dapeng.rpc.common.cb.ResponseCallBackV2;
import cn.dapeng.rpc.common.serial.SerializeFactory;
import cn.dapeng.rpc.common.util.MagicNumberUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

public class ClientV2 {

    public static RpcResponse call(String s) {
        ProxyCall proxyCall = proxyGet(ProxyCall.class, new InetSocketAddress("192.168.110.133", 9002));
        return proxyCall.call(s);
    }

    public static <T> T proxyGet(Class<T> clazz, InetSocketAddress address) {

        Class<?>[] methodInfo = {clazz};
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), methodInfo, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                // 重新定义协议 魔幻数字+body
                long requestId = Math.abs(UUID.randomUUID().getLeastSignificantBits());
                RpcRequest rpcRequest = new RpcRequest(requestId, clazz.getName(), method.getName(), method.getParameterTypes(), args);

                byte[] body = SerializeFactory.serial(rpcRequest, 1);
                int magicNum = MagicNumberUtil.getMagic(Constant.VERSION, 0, body.length, Constant.SERIAL_VERSION, RequestTypeEnum.REQUEST.ordinal());

                CompletableFuture<RpcResponse> future = new CompletableFuture<>();
                ResponseCallBackV2.addCallback(requestId, future);
                NioSocketChannel clientChannel = ClientFactoryV2.getInstance().getClient(address);

                ByteBuf sendBuf = PooledByteBufAllocator.DEFAULT.directBuffer(Integer.BYTES + body.length);
                sendBuf.writeInt(magicNum);
                sendBuf.writeBytes(body);
                clientChannel.writeAndFlush(sendBuf).sync();

                return future.get();
            }
        });
    }

}
